package com.tc.service.impl;

import com.tc.client.ItemMy;
import com.tc.dto.Result;
import com.tc.factory.ResultFactory;
import com.tc.mapper.DainaMapper;
import com.tc.messageDto.Daina;
import com.tc.messageDto.DainaCenter;
import com.tc.messageDto.Message;
import com.tc.myDto.UserApp;
import com.tc.service.DainaService;
import com.tc.utils.MyWay;
import com.tc.utils.TransformHashCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class DainaServiceImpl implements DainaService {
    @Autowired
    private DainaMapper dainaMapper;
    @Autowired
    private ResultFactory result;
    @Autowired
    private ExecutorService executorService;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private ItemMy itemMy;
    //定义唯一锁
    private final Lock lock=new ReentrantLock();

    //发布代拿
    @Override
    public Result addDaina(Daina daina, HttpServletRequest request) {
        //首先获取代拿发布者id
        String id=request.getHeader("id");
        //首先创建代拿中心的订单对象
        DainaCenter dainaCenter=DainaCenter.builder()
                                           .id(MyWay.createId(id))
                                           .userId(Integer.valueOf(id))
                                           .publish(LocalDateTime.now())
                                           .orderNumber(daina.getUuid())
                                           .username(daina.getName())
                                           .place(daina.getPlace())
                                           .iphone(daina.getIphone())
                                           .build();
        //这里一定是先存mysql,因为要马上分页查询
        dainaMapper.addDaina(dainaCenter);
        executorService.submit(()->{
            //然后再使用异步存储redis
            redisTemplate.opsForHash()
                         .put(id,daina.getUuid(),dainaCenter);
        });
        return result.success();
    }

    //获取所有代拿订单,直接查mysql
    @Override
    public Result getAllDaina(Integer number) {
        List<DainaCenter> list=dainaMapper.getAllDaina(number);
        return result.success(list);
    }

    //获取代拿订单
    @Transactional
    @Override
    public Result addMyDaina(DainaCenter dainaCenter, HttpServletRequest request) {
        //首先获取代拿执行者的id
        String takeUserId=request.getHeader("id");
        //首先要判断此订单是否已经被人代拿,这里存在线程安全,所以直接上锁
        DainaCenter r;
        lock.lock();
        try{
            //注意此时,订单不存在于订单没被接取情况相同,所以只能获取整条数据
            r=dainaMapper.selectMakeId(dainaCenter.getId());
            //根据不同情况执行不同操作,这里要小心空指针,r一定不能为null
            if(r!=null && r.getTakeUser()==null){//此时说明没有被人代拿,可以添加至我的代拿
                //直接在代拿大厅移除该订单,必须保证订单被删除才能释放锁!
                dainaMapper.deleteDainaOrder(dainaCenter.getOrderNumber());
                //然后异步操作,快速释放锁
                executorService.submit(()->{
                    //然后给该代拿订单赋值代拿执行者
                    dainaCenter.setTakeUser(Integer.valueOf(takeUserId));
                    //然后存储自己的代拿订单到redis
                    redisTemplate.opsForHash().put("myDaina_"+takeUserId,
                                                    dainaCenter.getOrderNumber(),
                                                    dainaCenter);
                    //生成二者唯一会话
                    Integer id=TransformHashCode.chatSessionId(Integer.valueOf(takeUserId), dainaCenter.getUserId());
                    //存储数据库,注意此时可能会重复,所以先查询
                    if(dainaMapper.selectSessionId(id)==null){
                        //此时说明不存在此会话,先添加
                        dainaMapper.addChatSession(id,Integer.valueOf(takeUserId),dainaCenter.getUserId());
                    }
                    //给用户发送消息,由于对首尾元素操作快，故选择双向链表,一定是从右边进列表
                    redisTemplate.opsForList().rightPush(("sessionId_"+id),
                                                        new Message(
                                                               "您好,订单号为:"+dainaCenter.getOrderNumber()+"的订单已被我接取",
                                                                takeUserId
                                                        ));
                });
            }
        }finally {
            lock.unlock();
        }
        return r!=null && r.getTakeUser()==null?
               result.success("抢单成功")
               :
               result.error("抢单失败");
    }

    //获取我的所有代拿
    @Override
    public Result selectAllMyDaina(HttpServletRequest request) {
        Map<Object, Object> map=redisTemplate.opsForHash()
                                             .entries("myDaina_"+request.getHeader("id"));
        return result.success(map);
    }

    //获取所有的聊天对象(一)
    @Override
    public Result getMyselfChatFirst(HttpServletRequest request) {
        //获取主键id
        Integer id=Integer.valueOf(request.getHeader("id"));
        //创建容器装所有的聊天对象
        List<UserApp> list=new ArrayList<>();
        //查询与自己所有的聊天对象,查询userid2
        List<Integer> ids=dainaMapper.getChatObj1(id);
        //首先判断是否存在
        if(ids!=null && ids.size()>0){
            //遍历集合,把找到的聊天对象信息存入容器
            for (Integer integer : ids) {
                list.add(itemMy.selectInformation(integer));
            }
        }
        return result.success(list);
    }

    //获取所有的聊天对象(二)
    @Override
    public Result getMyselfChatSecond(HttpServletRequest request) {
        //获取主键id
        Integer id=Integer.valueOf(request.getHeader("id"));
        //创建容器装所有的聊天对象
        List<UserApp> list=new ArrayList<>();
        //查询与自己所有的聊天对象,查询userid1
        List<Integer> ids=dainaMapper.getChatObj2(id);
        //判断是否存在
        if(ids!=null && ids.size()>0 ){
            for (Integer integer : ids) {
                //每一个键都都要被记录，因为这代表一次聊天,先分割
                list.add(itemMy.selectInformation(integer));
            }
        }
        return result.success(list);
    }

    //获取指定对象的聊天记录
    @Override
    public Result getChatMessage(Integer id, HttpServletRequest request) {
        //获取主键id
        String chatId=request.getHeader("id");
        //获取唯一会话id
        Integer chatSessionId=TransformHashCode.chatSessionId(id,Integer.valueOf(chatId));
        //根据会话直接查询redis
        List<Object> list=redisTemplate.opsForList()
                                       .range("sessionId_"+chatSessionId,0,-1);
        return result.success(list);
    }

    //获取指定聊天对象的信息
    @Override
    public Result getUserMessage(Integer id) {
        //直接查询对象个人信息
        return result.success(itemMy.selectInformation(id));
    }
}
